package com.apache.gateway.route;

import com.alibaba.fastjson.JSON;
import com.apache.api.vo.ResultEntity;
import com.apache.cache.service.impl.LoadCacheFactory;
import com.apache.gateway.config.JedisUtil;
import com.apache.rpc.common.LoadRpcService;
import com.apache.tools.ConfigUtil;
import com.apache.tools.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.*;

/**
 * @description: 动态路由service
 * @author: Hou Dayu
 * @date: Created in 2021/2/2
 */
@Service
public class DynamicRouteService implements ApplicationEventPublisherAware, CommandLineRunner {

    private Logger logger = LoggerFactory.getLogger("gateway-logs");
    private List<String> routeIds = new ArrayList();

    @Autowired
    @Qualifier("redisRouteDefinitionRepository")
    private RouteDefinitionRepository routeDefinitionWriter;

    @Autowired
    private JedisUtil jedisUtil;

    private ApplicationEventPublisher publisher;

    @Override
    public void run(String... args) throws Exception {
        try {
            this.loadRouteConfig(true);//加载路由
        } catch (Exception e){
            logger.error("Gateway configuration information loading error:"+e.getMessage());
        }
    }

    public String loadRouteConfig(boolean isInit){
        List<Object> routes = getDefRoutes();
        getRoutes(routes,isInit);
        logger.info("网关配置信息：=====>"+ JSON.toJSONString(routes));
        routes.forEach(gatewayRoute -> {
            RouteDefinition definition = saveDefine((Map<String,String>)gatewayRoute);
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        });
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "success";
    }

    private void getRoutes(List<Object> routes, boolean init){
        if(init){
            List<Object> objs = getRomeRouteDatas();
            if(!StrUtil.isEmpty(objs)){
                routes.addAll(objs);
            }
        } else {
            List<String> keys = LoadCacheFactory.getInstance().getCacheManager("zuul.routes").getAllKeys();
            if (null != keys && !keys.isEmpty()) {
                List<Object> objs = LoadCacheFactory.getInstance().getCacheManager("zuul.routes").getObjects(keys);
                routes.addAll(objs);
            }
        }
    }

    private RouteDefinition saveDefine(Map<String, String> param) {
        RouteDefinition definition = new RouteDefinition();
        definition.setId(param.get("infoId"));
        Map<String, String> predicateParams = new HashMap<>(8);
        PredicateDefinition predicate = new PredicateDefinition();

        predicate.setName("Path");
        predicateParams.put("pattern",param.get("path"));
        predicateParams.put("pathPattern", param.get("path"));
        predicateParams.put("sensitiveHeaders","*");
        predicate.setArgs(predicateParams);
        List<FilterDefinition> filters = new ArrayList<>();
        FilterDefinition filterDef = new FilterDefinition();
        filterDef.setName("RequestRateLimiter");
        Map<String, String> filter2 = new HashMap<>(8);
        String limiterRate = StrUtil.doNull(param.get("rateLimit"), "1000");
        // 令牌桶流速,允许用户每秒处理多少个请求
        filter2.put("redis-rate-limiter.replenishRate", limiterRate);
        //令牌桶容量，允许在一秒钟内完成的最大请求数
        filter2.put("redis-rate-limiter.burstCapacity", (2*Integer.parseInt(limiterRate))+"");
        // 限流策略(#{@BeanName})
        filter2.put("key-resolver", "#{@apiKeyResolver}");//"#{@idKeyResolver}");//
        //熔断机制 需要引spring-cloud-starter-netflix-hystrix
        filter2.put("name","default");
        filter2.put("fallbackUri","forward:/fallback");//fallback 被调用时，请求将转发到/incaseoffailureuset这个 URI
        filterDef.setArgs(filter2);
        filters.add(filterDef);
        definition.setFilters(filters);
        if(StrUtil.isNotNull(param.get("version"))) {//权重设置
            PredicateDefinition Weight = new PredicateDefinition();
            Map<String, String> filter1Params = new HashMap<>(8);
            //filter1Params.put(param.get("version"), param.get("weight"));
            filter1Params.put("_genkey_0",param.get("version"));
            filter1Params.put("_genkey_1",param.get("weight"));
            Weight.setName("Weight");
            Weight.setArgs(filter1Params);
            definition.setPredicates(Arrays.asList(predicate,Weight));
        } else {
            definition.setPredicates(Arrays.asList(predicate));
        }
        URI uri = null;
        if(!"serviceid".equalsIgnoreCase(param.get("routeType"))){//http地址
            uri = UriComponentsBuilder.fromHttpUrl(param.get("url")).build().toUri();
        } else {//注册中心
            uri = UriComponentsBuilder.fromUriString("lb://"+param.get("serviceId")).build().toUri();
        }
        definition.setUri(uri);
        //definition.setOrder(Integer.valueOf(StrUtil.doNull(param.get("routeOrder"),"1")));
        jedisUtil.setRoutsCache("",param.get("infoId"),param);
        return definition;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    private List<Object> getDefRoutes(){
        List<Object> datas = new ArrayList(2);
        Map<String, String> sso = new HashMap();
        sso.put("path","/sso/**");
        sso.put("infoId","sso-server");
        sso.put("rateLimit","1000");
        sso.put("routeType","serviceid");
        sso.put("serviceId","SSO-SERVER");
        Map<String, String> ius = new HashMap();
        ius.put("infoId","ius-server");
        ius.put("path","/ius/**");
        ius.put("rateLimit","1000");
        ius.put("routeType","serviceid");
        ius.put("serviceId","IUS-SERVER");
        ius.put("ifHystrix","0");
        ius.put("ifLog","1");
        ius.put("loginUrl","/ius/login.jsp");
        datas.add(sso);
        datas.add(ius);
        return datas;
    }

    private List<Object> getRomeRouteDatas() {
        Map<String,Object> params = new HashMap();
        params.put("datasource", "plateform");
        params.put("modelTypes", "s_sysZuulRoute");
        params.put("resultObjType", "list");
        params.put("resultType", "objInfo");
        params.put("sqlKeyId", "sqlKeyId");
        params.put("sysZuulRoute.w_enabled", "1");
        params.put("sysPass", ConfigUtil.getInstance().interfacePass());
        ResultEntity res = LoadRpcService.service().doService("iusService","dymicSql","xml",params,null);
        if(!StrUtil.isEmpty(res)){
            return (List)res.getEntity();
        }
        return null;
    }
}
